package com.wei.czz.framework.annex.helper;

import com.alibaba.fastjson2.JSON;
import com.wei.czz.common.dto.annex.AnnexGroupDto;
import com.wei.czz.common.dto.annex.AnnexGroupViewDto;
import com.wei.czz.common.dto.annex.BusinessAnnexDto;
import com.wei.czz.common.enums.CommonEnum;
import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.enums.annex.AnnexConditionEnum;
import com.wei.czz.common.enums.annex.AnnexFieldCompareModeEnum;
import com.wei.czz.common.exception.CzzException;
import com.wei.czz.common.po.annex.BusinessAnnexPo;
import com.wei.czz.common.vo.annex.AnnexConfigConditionVo;
import com.wei.czz.common.vo.annex.AnnexConfigFieldVo;
import com.wei.czz.common.vo.annex.AnnexConfigJsonVo;
import com.wei.czz.framework.annex.entity.AnnexConfigEntity;
import com.wei.czz.framework.annex.entity.AnnexGroupEntity;
import com.wei.czz.framework.annex.entity.AnnexPreEntity;
import com.wei.czz.framework.annex.entity.BusinessAnnexEntity;
import com.wei.czz.framework.annex.service.AnnexConfigService;
import com.wei.czz.framework.annex.service.AnnexGroupService;
import com.wei.czz.framework.annex.service.AnnexPreService;
import com.wei.czz.framework.annex.service.BusinessAnnexService;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParseException;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2024-01-18 16:23:15
 * className: AnnexHelper
 * version: 1.0
 * description:
 */
@Component
@AllArgsConstructor
public class AnnexHelper {

    private static final Logger log = LoggerFactory.getLogger(AnnexHelper.class);

    private final AnnexConfigService annexConfigService;

    private final AnnexGroupService annexGroupService;

    private final AnnexPreService annexPreService;

    private final BusinessAnnexService businessAnnexService;

    public List<AnnexGroupViewDto> getBusinessAnnex(Long relationId, String relationCode, Integer classify) {
        /*
            根据关联主键&关联编码&附件分类，查询已关联的附件配置
         */

        /*
            获取业务已关联的附件配置
         */
        List<BusinessAnnexEntity> businessAnnexList = businessAnnexService.getBusinessAnnexList(relationId,
                relationCode, classify);
        if (businessAnnexList.isEmpty()) {
            return Collections.emptyList();
        }
        Long preId = null;
        Set<Long> configIdSet = new HashSet<>();
        for (BusinessAnnexEntity businessAnnex : businessAnnexList) {
            preId = businessAnnex.getPreId();
            configIdSet.add(businessAnnex.getConfigId());
        }
        log.info("preId={} 业务已关联附件配置数量={}", preId, configIdSet.size());

        /*
            获取附件动态配置分组
         */
        List<AnnexGroupEntity> annexGroupList = annexGroupService.getAnnexGroupList(preId);

        /*
            获取附件动态配置分组
         */
        List<AnnexConfigEntity> annexConfigList = annexConfigService.getAnnexConfigList(preId, null);

        Map<Long, List<AnnexConfigFieldVo>> fieldMap = new HashMap<>();
        for (AnnexConfigEntity annexConfig : annexConfigList) {
            if (!configIdSet.contains(annexConfig.getId())) {
                continue;
            }
            AnnexConfigJsonVo annexConfigJsonVo = JSON.parseObject(annexConfig.getConfigJson(),
                    AnnexConfigJsonVo.class);

            List<AnnexConfigFieldVo> fieldList = annexConfigJsonVo.getFieldList();

            List<AnnexConfigFieldVo> annexConfigFieldVoList = fieldMap.computeIfAbsent(
                    annexConfig.getGroupId(),
                    key -> new ArrayList<>()
            );
            annexConfigFieldVoList.addAll(fieldList);
        }

        // 构造分组结构树
        return this.buildAnnexGroupTree(annexGroupList, fieldMap);
    }


    public BusinessAnnexDto getBusinessAnnex(Integer classify, Map<String, Object> paramMap) {
        /*
            根据附件分类和过滤参数，查询对应的附件配置
         */

        /*
            获取附件动态配置前置
         */
        AnnexPreEntity annexPre = annexPreService.getLastAnnexPre(classify);

        /*
            获取附件动态配置分组
         */
        List<AnnexGroupEntity> annexGroupList = annexGroupService.getAnnexGroupList(annexPre.getId());
        log.info("附件动态配置分组数量={}", annexGroupList.size());

        /*
            获取附件动态配置
         */
        List<AnnexConfigEntity> annexConfigList = annexConfigService.getAnnexConfigList(annexPre.getId(),
                null);
        log.info("附件动态配置分组数量={}", annexConfigList.size());

        List<BusinessAnnexPo> businessAnnexPoList = new ArrayList<>();
        Map<Long, List<AnnexConfigFieldVo>> fieldMap = new HashMap<>();

        for (AnnexConfigEntity annexConfig : annexConfigList) {
            AnnexConfigJsonVo annexConfigJsonVo = JSON.parseObject(annexConfig.getConfigJson(),
                    AnnexConfigJsonVo.class);

            // 判断是否满足附件条件
            boolean bool = this.validAnnexCondition(annexConfig.getId(), annexConfigJsonVo, paramMap);
            if (!bool) {
                continue;
            }
            List<AnnexConfigFieldVo> fieldList = annexConfigJsonVo.getFieldList();

            List<AnnexConfigFieldVo> annexConfigFieldVoList = fieldMap.computeIfAbsent(
                    annexConfig.getGroupId(),
                    key -> new ArrayList<>()
            );
            annexConfigFieldVoList.addAll(fieldList);

            for (AnnexConfigFieldVo annexConfigFieldVo : fieldList) {
                BusinessAnnexPo businessAnnexPo = new BusinessAnnexPo();
                businessAnnexPo.setPreId(annexPre.getId())
                        .setConfigId(annexConfig.getId())
                        .setAnnexCode(annexConfigFieldVo.getCode());
                businessAnnexPoList.add(businessAnnexPo);
            }
        }

        // 构造分组结构树
        List<AnnexGroupViewDto> annexGroupViewList = this.buildAnnexGroupTree(annexGroupList, fieldMap);

        BusinessAnnexDto businessAnnexDto = new BusinessAnnexDto();
        businessAnnexDto.setId(annexPre.getId())
                .setAnnexGroupList(annexGroupViewList)
                .setBusinessAnnexPoList(businessAnnexPoList);
        return businessAnnexDto;
    }

    /**
     * 验证附件配置条件是否满足
     * @param configId          附件配置主键
     * @param annexConfigJsonVo 附件配置
     * @param paramMap          参数映射
     * @return 验证结果[true-通过 false-拒绝]
     */
    private boolean validAnnexCondition(Long configId, AnnexConfigJsonVo annexConfigJsonVo,
                                        Map<String, Object> paramMap) {
        log.info("校验请求参数是否满足附件动态配置条件。configId={}", configId);

        List<AnnexConfigConditionVo> conditionList = annexConfigJsonVo.getConditionList();
        String expression = annexConfigJsonVo.getExpression();

        StringBuilder sb = new StringBuilder();
        StringBuilder paramFieldSb = new StringBuilder("v");

        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

        // SpringEL语句解析器
        ExpressionParser parser = new SpelExpressionParser();
        // SpringEL上下文
        EvaluationContext ctx = new StandardEvaluationContext();

        for (AnnexConfigConditionVo annexConfigConditionVo : conditionList) {
            AnnexConditionEnum annexConditionEnum = AnnexConditionEnum.get(annexConfigConditionVo.getValue());
            if (Objects.isNull(annexConditionEnum)) {
                log.error("字段条件枚举对象不存在。configId={} value={}", configId, annexConfigConditionVo.getValue());
                throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "附件字段条件枚举不存在");
            }
            AnnexFieldCompareModeEnum annexFieldCompareModeEnum =
                    AnnexFieldCompareModeEnum.get(annexConfigConditionVo.getCompareMode());
            if (Objects.isNull(annexFieldCompareModeEnum)) {
                log.error("字段比较方式枚举对象不存在。configId={} value={}", configId, annexConfigConditionVo.getValue());
                throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "附件字段比较方式枚举不存在");
            }
            // 参数字段名称
            String paramKey = paramFieldSb.delete(1, paramFieldSb.length()).append(annexConditionEnum.getField())
                    .toString();
            // 参数值
            Object paramValue = paramMap.get(annexConditionEnum.getField());

            switch (annexFieldCompareModeEnum) {
                case EQUALS_NUMBER:
                case NOT_EQUALS_NUMBER:
                    // 记录参与计算的参数
                    sb.append('(').append(annexConditionEnum.getField()).append('=')
                            .append(annexConfigConditionVo.getEqualsNumber()).append(';')
                            .append(paramKey).append('=').append(JSON.toJSONString(paramValue))
                            .append(')');
                    // 设置计算参数
                    ctx.setVariable(annexConditionEnum.getField(),  annexConfigConditionVo.getEqualsNumber());
                    ctx.setVariable(paramKey, paramValue);
                    break;
                case EQUALS_STRING:
                case NOT_EQUALS_STRING:
                    sb.append('(').append(annexConditionEnum.getField()).append('=')
                            .append(annexConfigConditionVo.getEqualsString()).append(';')
                            .append(paramKey).append('=').append(JSON.toJSONString(paramValue))
                            .append(')');

                    ctx.setVariable(annexConditionEnum.getField(), annexConfigConditionVo.getEqualsString());
                    ctx.setVariable(paramKey, paramValue);
                    break;
                case CONTAIN_NUMBER:
                case NOT_CONTAIN_NUMBER:
                    sb.append('(').append(annexConditionEnum.getField()).append('=')
                            .append(annexConfigConditionVo.getNumberList()).append(';')
                            .append(paramKey).append('=').append(JSON.toJSONString(paramValue))
                            .append(')');

                    ctx.setVariable(annexConditionEnum.getField(), annexConfigConditionVo.getNumberList());
                    ctx.setVariable(paramKey, paramValue);
                    break;
                case CONTAIN_STRING:
                case NOT_CONTAIN_STRING:
                    sb.append('(').append(annexConditionEnum.getField()).append('=')
                            .append(annexConfigConditionVo.getContainString()).append(';')
                            .append(paramKey).append('=').append(JSON.toJSONString(paramValue))
                            .append(')');

                    ctx.setVariable(annexConditionEnum.getField(), annexConfigConditionVo.getContainString());
                    ctx.setVariable(paramKey, paramValue);
                    break;
                case RANGE_NUMBER:
                    sb.append('(').append(annexConditionEnum.getField()).append("Min=")
                            .append(annexConfigConditionVo.getMinNumber()).append(';')
                            .append(annexConditionEnum.getField()).append("Max=")
                            .append(annexConfigConditionVo.getMaxNumber()).append(';')
                            .append(paramKey).append('=').append(JSON.toJSONString(paramValue))
                            .append(')');

                    ctx.setVariable(annexConditionEnum.getField().concat("Min"),
                            annexConfigConditionVo.getMinNumber());
                    ctx.setVariable(annexConditionEnum.getField().concat("Max"),
                            annexConfigConditionVo.getMaxNumber());
                    ctx.setVariable(paramKey, paramValue);
                    break;
                case RANGE_DATETIME:
                    sb.append('(').append(annexConditionEnum.getField()).append("Min=")
                            .append(annexConfigConditionVo.getMinDateTime().format(dateTimeFormatter)).append(';')
                            .append(annexConditionEnum.getField()).append("Max=")
                            .append(annexConfigConditionVo.getMaxDateTime().format(dateFormatter)).append(';')
                            .append(paramKey).append('=').append(JSON.toJSONString(paramValue))
                            .append(')');

                    ctx.setVariable(annexConditionEnum.getField().concat("Min"),
                            annexConfigConditionVo.getMinDateTime());
                    ctx.setVariable(annexConditionEnum.getField().concat("Max"),
                            annexConfigConditionVo.getMaxDateTime());
                    ctx.setVariable(paramKey, paramValue);
                case RANGE_DATE:
                    sb.append('(').append(annexConditionEnum.getField()).append("Min=")
                            .append(annexConfigConditionVo.getMinDate().format(dateTimeFormatter)).append(';')
                            .append(annexConditionEnum.getField()).append("Max=")
                            .append(annexConfigConditionVo.getMaxDate().format(dateFormatter)).append(';')
                            .append(paramKey).append('=').append(JSON.toJSONString(paramValue))
                            .append(')');

                    ctx.setVariable(annexConditionEnum.getField().concat("Min"),
                            annexConfigConditionVo.getMinDate());
                    ctx.setVariable(annexConditionEnum.getField().concat("Max"),
                            annexConfigConditionVo.getMaxDate());
                    ctx.setVariable(paramKey, paramValue);
                    break;
                default:
                    break;
            }
        }
        log.info("附件配置={} 表达式：【{}】 表达式参数：{}", configId, expression, sb);
        Boolean bool = Boolean.FALSE;
        try {
            bool = parser.parseExpression(expression).getValue(ctx, Boolean.class);
        } catch (ParseException parseException) {
            log.error("【{}】附件动态配置条件EL表达式解析失败。expression={} message={}", configId, expression,
                    parseException.getMessage(), parseException);
        } catch (EvaluationException evaluationException) {
            log.error("【{}】附件动态配置条件EL表达式执行失败。expression={} message={}", configId, expression,
                    evaluationException.getMessage(), evaluationException);
        }
        log.info("附件配置={} 执行结果：{}", configId, bool);
        return Boolean.TRUE.equals(bool);
    }

    /**
     * 构造附件动态配置分组结构树
     * @param annexGroupList 附件动态配置分组列表
     * @param fieldMap       附件动态配置分组与附件动态配置映射对象
     * @return 附件动态配置分组结构树
     */
    private List<AnnexGroupViewDto> buildAnnexGroupTree(List<AnnexGroupEntity> annexGroupList,
                                                        Map<Long, List<AnnexConfigFieldVo>> fieldMap) {
        List<AnnexGroupViewDto> annexGroupViewList = new ArrayList<>();
        Map<String, AnnexGroupViewDto> annexGroupMap = new HashMap<>();
        List<AnnexGroupViewDto> lastOptList = new ArrayList<>();

        for (AnnexGroupEntity annexGroup : annexGroupList) {
            AnnexGroupViewDto annexGroupViewDto = AnnexGroupViewDto.map(annexGroup);
            // 设置分组下面的附件列表
            annexGroupViewDto.setFieldList(fieldMap.get(annexGroup.getId()));

            annexGroupMap.put(annexGroupViewDto.getId(), annexGroupViewDto);

            AnnexGroupViewDto parentAnnexGroupViewDto = annexGroupMap.get(annexGroupViewDto.getParentId());
            if (Objects.isNull(parentAnnexGroupViewDto)) {
                if (CommonEnum.ZERO.getLongValue().equals(annexGroup.getParentId())) {
                    annexGroupViewList.add(annexGroupViewDto);
                } else {
                    lastOptList.add(annexGroupViewDto);
                }
                continue;
            }
            TreeSet<AnnexGroupDto> children = parentAnnexGroupViewDto.getChildren();
            if (Objects.isNull(children)) {
                children = new TreeSet<>();
                parentAnnexGroupViewDto.setChildren(children);
            }
            children.add(annexGroupViewDto);
        }
        for (AnnexGroupViewDto annexGroupViewDto : lastOptList) {
            AnnexGroupViewDto parentAnnexGroupViewDto = annexGroupMap.get(annexGroupViewDto.getParentId());
            if (Objects.isNull(parentAnnexGroupViewDto)) {
                log.info("父级附件动态配置分组不存在。{}", annexGroupViewDto);
                continue;
            }
            TreeSet<AnnexGroupDto> children = parentAnnexGroupViewDto.getChildren();
            if (Objects.isNull(children)) {
                children = new TreeSet<>();
                parentAnnexGroupViewDto.setChildren(children);
            }
            children.add(annexGroupViewDto);
        }
        return annexGroupViewList;
    }

}
